using System;
using System.Collections.Generic;
using System.Linq;
using EnvDTE80;
using LazyPlugins.Common;

namespace LazyPlugins.AttachManager
{
    internal enum AttachType
    {
        Managed,
        Native,
        ManagedAndNative
    }

    internal enum AttachResult
    {
        Attached,
        NotRunning,
        BeingDebugged,
        UnexpectedError
    }

    public class ProcessAttacher
    {
        private readonly IDebuggerInfo _debuggerInfo;

        #region private

        private readonly Dictionary<AttachType, string> attachTypesMap;
        

	    #endregion        

        #region ctor

        public ProcessAttacher(IDebuggerInfo debuggerInfo )
        {
            _debuggerInfo = debuggerInfo;

            attachTypesMap = new Dictionary<AttachType, string>
                                 {
                                     {AttachType.Managed, "Managed"},
                                     {AttachType.Native, "Native"},
                                     {AttachType.ManagedAndNative, "Managed/Native"}
                                 };
        }

        #endregion        
    
        #region private methods

        private AttachResult Attach(AttachType attachType, string processName)
        {
            string engine = attachTypesMap[attachType];

            if (IsBeingDebugged(processName))
            {
                return AttachResult.BeingDebugged;
            }
            try
            {
                Engine eng = _debuggerInfo.Engines.Item(engine);
                Process2 proc = _debuggerInfo.GetProcess(processName);
                proc.Attach2(eng);
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("Invalid index."))
                {
                    return AttachResult.NotRunning;
                }
            }

            return AttachResult.Attached;

        }

        private static void DisplayResult(AttachResult attachResult)
        {
            switch(attachResult)
            {
                case AttachResult.BeingDebugged:
                    System.Windows.Forms.MessageBox.Show("The process is already being debbuged");
                    break;

                case AttachResult.NotRunning:
                    System.Windows.Forms.MessageBox.Show("The process is not running");
                    break;

                case AttachResult.Attached:
                    break;
                case AttachResult.UnexpectedError:
                    System.Windows.Forms.MessageBox.Show("An unknown error occurred while trying to attach to process");
                    break;
            }
        }

        private bool IsBeingDebugged(string processName)
        {
            return _debuggerInfo.DebuggedProcesses.
                    Any(process => process.Name.IndexOf(processName) != -1);
        }

        private AttachResult PessimisticAttach(AttachType attachType, string processName, int waitTimeout)
        {
            AttachResult res = Attach(attachType, processName);

            DateTime timeout = DateTime.Now.AddSeconds(waitTimeout);

            while(res == AttachResult.NotRunning && timeout > DateTime.Now)
            {
                res = Attach(attachType, processName);
                System.Threading.Thread.Sleep(100);
            }
            return res;
        }

    	#endregion        

        #region public methods

        public void OptimisticAttachNative(string processName)
        {
            AttachResult res = Attach(AttachType.Native, processName);
            DisplayResult(res);
        }

        public void PessimisticAttachNative(string processName, int waitTimeout)
        {
            AttachResult res = PessimisticAttach(AttachType.Native, processName, waitTimeout);
            DisplayResult(res);
        }

        public void OptimisticAttachManaged(string processName)
        {
            AttachResult res = Attach(AttachType.Managed, processName);
            DisplayResult(res);
        }

        public void PessimisticAttachManaged(string processName, int waitTimeout)
        {
            AttachResult res = PessimisticAttach(AttachType.Managed, processName, waitTimeout);
            DisplayResult(res);
        }

        public void OptimisticAttachManagedAndNative(string processName)
        {
            AttachResult res = Attach(AttachType.ManagedAndNative, processName);
            DisplayResult(res);
        }

        public void PessimisticAttachManagedAndNative(string processName, int waitTimeout)
        {
            AttachResult res = PessimisticAttach(AttachType.ManagedAndNative, processName, waitTimeout);
            DisplayResult(res);
        }

        #endregion
    }
}

